C++11常用特性的总结

cpp软件架构狮 2018-07-13 22:52:51

C++11发布到现在已经很久了,网上的c++11的新特性的总结文章。本篇文章,也是自己在平时的工作中的使用,对C++11的一些总结,经验和感悟。整理出来分享出来,希望能够帮助大家能够快速的熟悉这些新特性。

C++11常用特性的总结

1 关键字及新语法

1.1 auto关键字及用法

1.2 nullptr关键字及用法

1.3 for循环语法

2 STL容器

2.1 std::array

2.2 std::forward_list

2.3 std::unordered_map

2.4 std::unordered_set

3 多线程

3.1 std::thread

3.2 st::atomic

3.3 std::condition_variable

4、智能指针内存管理

4.1 std::shared_ptr

4.2 std::weak_ptr

5 其他

5.1 std::function、std::bind封装可执行对象

5.2 lamda表达式

1 新关键字和语法

c++11比98的版本增加了许多关键字和新的特性。很多人觉得这些语法可有可无,没有新特性也可以用传统C++去实现。可是你要知道我是一个对待新技术总是抱着渴望而热衷的态度对待的人,也许就像很多人说的,用传统语法也可以实现,但新技术可以让你的设计更完美,存在就是合理的。这就如同在原来的维度里,你要完成一件事情,需要很多个复杂的步骤,但在新语法特性里,你可以从另外的维度,很干脆,直接就把原来很复杂的问题用很简单的方法解决了,我想着就是新的技术带来的一些编程体验上非常好的感觉。大家也不要觉得代码写得越复杂就先显得越牛B,有时候在理解的基础上,尽量选择“站在巨人的肩膀上”,可能你会站得更高,也看得更远。不要总是在造轮子,别人已经造好的轮子,为何不直接拿过来用,况且自己造轮子未必会比别人造的好。

本文重点总结一些常用c++11新语法特点。后续会在本人理解的基础上,增加更多的知识。

1-1 auto关键字

闲话少说,直接上代码

auto Test(int x, int y)
{
	return x+y;
}
int main()
{
	auto i = 10;
	auto strs = "abc";
	auto ret = Test(1,2);
	std::cout << "i:" << i << std::endl;
	std::cout << "strs:" << strs << std::endl;
	std::cout << "re:" << ret << std::endl;
}

C++11常用特性的总结

1-2 nullptr关键字

增加新的关键字,肯定是需要增加的理由,这个理由就是空指针在98版本中是跟c是一样的。都是用NULL表示的,NULL在C库中是用“0”表示,void *test(int *index)和void *test(int index)的重载当传入参数NULL就会产生歧义,最终编译器会选择后者。为了解决这个问题,新标准中引入了此关键字

1-3 for语法

习惯java的同学之前使用C++的时候,抱怨没有Java那样foreach的用法。没错,在C++11之前,标准C++是无法做到的。熟悉boost库读者可能知道boost里面有foreach的宏定义BOOST_FOREACH,但个人觉得看起并不是那么美观。

直接代码展示

int main()
{
	int num[] = { 1,2,3,4,5,6,8 };
	std::cout << "num:" << std::endl;
	for (auto number : num)
	{
		std::cout << number << std::endl;
	}
}

2,STL容器

STL大家都知道,很好用。容器也比较多,11版本给人的感觉就是越来越完善了。可以提高我们在不同的场景中,有更多的选择。提高我们的开发效率。

2.1 std::array

直接上代码

#include 
int main()
{
	std::array Demo = { 1,3,4 };
	std::cout << "Demo:" << std::endl;
	for (auto it : Demo)
	{
		std::cout << it << std::endl;
	}
	int arrayDemoSize = sizeof(Demo);
	std::cout << "Demo size:" << DemoSize << std::endl;
	return 0;
}

2.2 std::forward_list

std::forward_list为C++新增的线性表,与list区别在于它是单向链表。我们在学习数据结构的时候都知道,链表在对数据进行插入和删除是比顺序存储的线性表有优势,因此在插入和删除操作频繁的应用场景中,使用list和forward_list比使用array、vector和deque效率要高很多。

直接上代码

#include 
int main()
{
	std::forward_list num = {1,2,3,4,5,4,4};
	std::cout << "num:" << std::endl;
	for (auto number : num)
	{
		std::cout << number << std::endl;
	}
	num.remove(4);
	std::cout << "numbers after remove:" << std::endl;
	for (auto number : num)
	{
		std::cout << number << std::endl;
	}
	return 0;
}

2.3 std::unordered_map

顾名思义他是一个map,std::unordered_map与std::map用法基本差不多,但STL在内部实现上有很大不同,std::map使用的数据结构为红黑树,也是二叉树的一种变种,而std::unordered_map内部是哈希表的实现方式,哈希map理论上查找效率为O(1)。但在存储效率上,哈希map需要增加哈希表的内存开销。

2.4 std::unordered_set

顾名思义它是一个set,std::unordered_set的数据存储结构通过哈希表来实现的,除此之外,std::unodered_set在插入时不会自动排序,这都是std::set表现不同的地方。

3 多线程

3.1 std::thread

在11版本出现之前,c++多线程编程中需要依赖系统或者第三方的接口实现。这样的方式影响了代码的移植性。在这个新版本的标准中将多线程部分加入。

直接上代码

#include 
void thrfun1()
{
	std::cout << "threadfun1 - 1 " << std::endl;
	std::this_thread::sleep_for(std::chrono::seconds(1));
	std::cout << "threadfun1 - 2" << std::endl;
}
void thrfun2(int iParam, std::string sParam)
{
	std::cout << "threadfun2 - 1" << std::endl;
	std::this_thread::sleep_for(std::chrono::seconds(5));
	std::cout << "threadfun2 - 2" << std::endl;
}
int main()
{
	std::thread t1(thrfun1);
	std::thread t2(thrfun2, 10, "abc");
	t1.join();
	std::cout << "join" << std::endl;
	t2.detach();
	std::cout << "detach" << std::endl;
}

3.2 std::atomic

原子类型在内核中用的比较多,从事内核开发的人员应该知道这个类型的作用。从功能上看,简单地说,原子数据类型不会发生数据竞争,能直接用在多线程中而不必我们用户对其进行添加互斥资源锁的类型。从实现上,大家可以理解为这些原子类型内部自己加了锁。

3.3 std::condition_variable

C++11中的std::condition_variable就像Linux下使用pthread_cond_wait和pthread_cond_signal,或者是跟信号量一样的功能,能够控制线程运行,一个线程在等待,另一个线程发送信号通知继续运行,多线程等待在多线程编程中使用非常频繁,经常需要等待一些异步执行的条件的返回结果。

#include 
#include  // std::thread
#include  // std::mutex, std::unique_lock
#include  // std::condition_variable
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void print_id(int id) {
	std::unique_lock lck(mtx);
	while (!ready) cv.wait(lck);
	// ...
	std::cout << "thread " << id << ' ';
}
void go() {
	std::unique_lock lck(mtx);
	ready = true;
	cv.notify_all();
}
int main()
{
	std::thread threads[10];
	// spawn 10 threads:
	for (int i = 0; i<10; ++i)
		threads[i] = std::thread(print_id, i);
	std::cout << "10 threads ready to race... ";
	go(); // go!
	for (auto& th : threads) th.join();
	return 0;
}

4 智能指针

我们知道在98版本中,auto_ptr智能指针,但是auto_ptr的设计存在缺陷,不建议大家使用,以免出现问题,得不偿失。

C++11移植了boost库中的智能指针的部分实现,如std::shared_ptr、std::weak_ptr等,当然,想boost::thread一样,C++11也修复了boost::make_shared中构造参数的限制问题。把智能指针添加为标准,本人觉得真的非常的方便,毕竟在C++中,智能指针在编程设计中使用的还是非常广泛。可以使我们的c++老大难问题,内存管理,提供了一种新的解决方式。

4.1 std::shared_ptr

std::shared_ptr 包装了new操作符动态申请的内存,可以自由拷贝,在实际使用中,应该是最多的一种智能指针类型了。

直接上代码

#include 
class TestPtr
{
	public:
	TestPtr()
	{
		;
	}
	~TestPtr()
	{
		std::cout << "~TestPtr()" << std::endl;
	}
};
int main()
{
	std::shared_ptr p1 = std::make_shared();
	std::cout << "1 ref:" << p1.use_count() << std::endl;
	{
		std::shared_ptr pTestPtr= p1;
		std::cout << "2 ref:" << p1.use_count() << std::endl;
	}
	std::cout << "3 ref:" << p1.use_count() << std::endl;
	return 0;
}

1、std::make_shared封装了new方法,boost::make_shared之前的原则是既然释放资源delete由智能指针负责,那么应该把new封装起来,否则会让人觉得自己调用了new,但没有调用delete,似乎与谁申请,谁释放的原则不符。C++也沿用了这一做法。

2、随着引用对象的增加std::shared_ptr p2 = p1,指针的引用计数有1变为2,当p2退出作用域后,p1的引用计数变回1,当main函数退出后,p1离开main函数的作用域,此时p1被销毁,当p1销毁时,检测到引用计数已经为1,就会在p1的析构函数中调用delete之前std::make_shared创建的指针。

4-2 std::weak_ptr

std::weak_ptr其实是为了解决std::shared_ptr在相互引用的情况下出现的问题,从而出现了这个类型的智能指针。

本页共191段,5586个字符,9407 Byte(字节)